/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package MultiThreaded;

import Modele.Dico;
import Vue.Vue;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 *
 * @author michaelrepiso
 */
public class MonteCarloSimulationMulti implements Callable {

    private Dico dico = new Dico();
    

    public MonteCarloSimulationMulti(Dico dico) {
        this.dico = dico;
    }

    /**
     *
     * @param CallPutFlag
     * @param S
     * @param X
     * @param T
     * @param r
     * @param b
     * @param v
     * @param nSteps
     * @param nSimulations
     * @return
     */
    public Double MonteCarloStandardOption(String CallPutFlag, Double S, Double X, Double T,
            Double r, Double b, Double v, int nSteps, int nSimulations, Vue vue) throws ExecutionException, InterruptedException {

        //declaration ressources

        //nombre cores disponibles
        int numberOfCores = Runtime.getRuntime().availableProcessors();
        //coefficient bloquant
        double blockingCoefficient = 0.1;
        //calcul nombre de thread à envoyer
        int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));
        System.out.println("nb thread : " + poolSize);


        ExecutorService pool = Executors.newFixedThreadPool(poolSize);
        Set<Future<Double>> set = new HashSet<>();

        //calculs
        dico.setDt( dico.getT() / dico.getnSteps() );
        vue.getProgressBar().setValue((1*100)/(5+nSimulations));
        vue.getProgressBar().setString(String.valueOf((1*100)/(5+nSimulations)) + " %");
        dico.setDrift( (dico.getB() - Math.pow(dico.getV(), 2) / 2) * dico.getDt() );
        vue.getProgressBar().setValue((2*100)/(5+nSimulations));
        vue.getProgressBar().setString(String.valueOf((2*100)/(5+nSimulations)) + " %");
        dico.setvSqrdt( dico.getV() * Math.sqrt( dico.getDt() ) );
        vue.getProgressBar().setValue((3*100)/(5+nSimulations));
        vue.getProgressBar().setString(String.valueOf((3*100)/(5+nSimulations)) + " %");
              
        //callputflag condition
        switch (dico.getCallPutFlag()) {
            case "c":
                dico.setZ(1);
                break;
            case "p":
                dico.setZ(-1);
                break;
            default:
                dico.setZ(0);
                break;
        }
        
        vue.getProgressBar().setValue((5*100)/(5+nSimulations));
        vue.getProgressBar().setString(String.valueOf((5*100)/(5+nSimulations)) + " %");
        
        //threads init
        Random rando = new Random();
        Double sum = 0.0;
        for (int i = 1; i <= nSimulations; i++) {

            Callable<Double> callable;
            callable = new MonteCarloSimulationMulti(dico);
            Future<Double> future = pool.submit(callable);
            set.add(future);
            
            vue.getProgressBar().setValue(((5+i)*100)/(5+nSimulations));
            vue.getProgressBar().setString(String.valueOf(((5+i)*100)/(5+nSimulations)) + " %");

        }

        for (Future<Double> futur : set) {
            sum  = sum + futur.get();
            vue.getProgressBar().setValue(100);
            vue.getProgressBar().setString(String.valueOf("Wait a moment please"));
        }
        
        vue.getProgressBar().setString(String.valueOf("Finish"));
        vue.getProgressBar().setValue(vue.getProgressBar().getMinimum());
        vue.getProgressBar().setString("");

        return Math.exp(-r * T) * (sum / nSimulations);
    }

    @Override
    public Double call() {
        Random rando = new Random();
        int j;
        double St;
        
        St = (double) dico.getS();
        //System.out.println(St);

        for (j = 1; j <= dico.getnSteps(); j++) {
            St = St * Math.exp(dico.getDrift() + dico.getvSqrdt() * rando.nextGaussian());
            //System.out.println(St + " " + Drift + " " + vSqrdt);
        }
        Double sum;
        sum = Math.max( dico.getZ() * (St - dico.getX()), 0);
        //vue.getProgressBar().setValue(((5 + i) * 100) / (5 + nSimulations));
        //vue.getProgressBar().setString(String.valueOf(((5 + i) * 100) / (5 + nSimulations)));
        //System.out.println(sum);
        return sum;
    }

}
